Repository navigation
Reference page fixes: sticky headings, parameters, tmux links and alignment - #157
Merged
Merged
Conversation
why: Long member lists scroll the Members heading and the active subgroup heading out of view, so a reader loses which group a member belongs to. The section row already clears the site header; the member headings need the same measured clearance without moving their IDs, permalinks or heading levels. what: - Wrap an owner's Members or Fields section in `.api-members` and stack its real h2 and group h3 below the measured header and section row. Both release at the section end, before inherited and related content. - Derive offsets, scroll margins and activation from the measured heading heights, so wrapped and enlarged text keep clearance. Stay in flow when the viewport cannot keep a reading area beside the bars. - Return a link request for a stuck owner or group heading to its source boundary, including a repeated hash. The anchor handler and the browser keep startup arrival, history traversal, typed hashes, modified clicks, new tabs and cross-page links. - Keep the bars in flow until the page is revealed, so the anchor handler's startup scroll is not skewed by a sticky heading in Firefox. - Return a keyboard-focused heading to its source box, but keep it sticky under a pointer press so the permalink click still lands. - Tint the opaque bar for the arrival cue instead of replacing its surface, including under reduced motion. - Confine sticky rules to screen media; leave scalar, single-group and empty owners without added headings. - Cover grouped, single, scalar and empty owners, release, wrapped headings, link requests, history, startup, the focus and arrival styles and the pending state in the api-page-contents tests.
why: A sticky box is aligned where it is stuck, not where its group starts. A link or typed hash for a group above the viewport landed at the end of that group in every engine, and Firefox did the same for a first link click when a frame rendered before the URL changed. The scroll padding counted only the header and section row, so Shift+Tab left focused links entirely under the Members and group bars. A strip of scrolled text also showed between the section row and the bars. what: - Hold the targeted Members or group heading in flow while a link click or a same-document fragment navigation scrolls, so the browser and the anchor handler align its source box. The Navigation API `navigate` event starts the hold before the browser scrolls a typed or scripted hash; a click in the layout starts it in browsers without that API. Animation frames end the hold, and every navigation signal restarts it. - Give focusable controls in the owner section a scroll margin equal to the measured bars above them: the Members bar, plus the control's own group bar. A heading permalink takes only the bars above its heading. - Cover the gap between the section row and the bars with the page background, using a sticky pseudo-element that adds no height. - Test the hold, its restart and release, the Navigation API entry point, the focus clearance and the cover in api-page-contents.test.ts. Verified: with the Swift Server page at 1800x1000, backward Shift+Tab left 2 of 14 stops under a bar in Chromium and Firefox and 7 in WebKit; none are now. A typed hash for the first group landed at scrollY 3609, 3723 and 3608 (Chromium, Firefox, WebKit), with the group box ending at 207, 94 and 208; it lands at 2677, 2678 and 2678 with the group starting at the reading line. A rail click to the first group from a later one ended at the group end in Firefox in 3 of 24 runs, and in 18 of 24 on a loaded machine; it ended there in 0 of 24 runs, twice, now.
why: The api-page-contents tests mock geometry in a headless DOM, so none of the focus, landing or typed hash failures of the sticky bars could fail them, and each engine aligns a sticky heading differently. what: - Add checkApiStickyHeadings. On the Swift Server page, at desktop and phone widths, it tabs backward through a stuck group, clicks a link to an earlier group once normally and once with the main thread busy, clicks a heading's link again from mid-group and from the group end, and types a hash for a group above the viewport. Each arrival must sit at the reading line, at the start of its group, uncovered, and reached by one scroll that never paints the group end. - Add checkApiStickyHeadingsFail. It removes the focus clearance and the Navigation API from a page and requires the check to reject, so the check cannot pass without them. - Run both in the default browser matrix, and in Chromium, Firefox and WebKit with `check-dev.mjs --api-sticky`. Document the mode in CONTRIBUTING.md. Verified: against the sticky commit alone, check-dev.mjs --api-sticky fails in Chromium and Firefox at "backward tab stop 11 is not entirely hidden by the sticky bars" and in WebKit at "backward tab stop 9 starts below the sticky bars". Run per scenario, a typed hash fails in all three engines. In Firefox a link on a busy page lands with the heading at 48 under the header (scrollY 3723), and a repeated link overshoots to 3915 instead of 3687. With the fix the command passes in all three engines, and the scenarios passed in three consecutive runs each.
why: Keyboard focus, the arrival hold and pending fonts put a member bar back in flow with position: static. The sticky cover that closes the gap under the section row is positioned, so it painted over the top of the static Members bar, its permalink and its focus outline. what: - Hold in-flow bars with position: relative and top: auto, so their z-index keeps them above the cover - Check in real browsers that a focused Members permalink is not covered, with a control that fails when the bar drops to static
why: Prose code literals link every name that resolves to one member. A keyword argument names a parameter of the call being described, so Ruby's `cancel:` linked to Cancellation#cancel, `clients: true` to Snapshot#clients, and `action:` to an unrelated Workspace member. what: - Add SymbolIndex.linkLiteral, which leaves `name:` and `name: value` plain and links other literals as before - Use it for HTML prose, and skip keyword arguments in Markdown export - Test that keyword arguments stay plain while the bare name still links
why: Python, Swift, Ruby and Lua listed only the parameters a doc comment described, so a Ruby Server.start page showed no Parameters section for executable:, config:, timeout: and cancel:, while C# listed its parameters with their types whether documented or not. what: - List each declared parameter with its type in every port - Link every parameter name in the signature to its entry - For Python, list the implementation's parameters, not each @overload stub's, and leave out an undocumented self or cls receiver; leave out Lua's self receiver too - Update the tests that pinned documented-only lists
why: The documentation picker named the tmux CLI Manual and C source reference, but the tmux sidebar listed only Guides, Concepts, Examples and Topics, and the overview cards called them Manual and Reference. what: - Add CLI Manual and C source reference links to the tmux sidebar, after its sections and before the notices - Title the overview cards CLI Manual and C source reference - Test the sidebar links for a pinned tmux version
…s, formats
why: A tmux command such as `list-clients` rendered as a plain link,
with no kind badge or hover card, while the API names beside it carried
both. `session_attached` and `destroy-unattached`, which the tmux manual
documents, were not linked at all.
what:
- Read options, hooks and format variables from the pinned manual per
release, anchored on the same headings the full manual page renders
- Resolve every command, option, hook and format-variable link through
tmuxEntityHref, so the destination of every kind changes in one place
- Link option and hook names written in code, and format variables that
are a whole code span or sit inside a #{...} expression; names the
release does not document, URI templates and calls stay plain
- Keep library symbols ahead of options, hooks and format variables in
HTML, prose Markdown and symbol Markdown, so `pane_id` still reaches a
Rust field where the page documents one
- Render each lone mention with the API kind badge (command $, option o,
hook h, format #) and add the four kinds to the badge palette in the
light, system dark and explicit dark themes; several names in one code
run stay links without badges, and Markdown keeps plain links
- Serve preview cards for command pages and for each manual entry by
anchor, with an "Open manual" action, and make manual links eligible
for hover previews outside the command index and heading permalinks
- Load the highlighter only when the manual is highlighted, so link
resolution no longer imports it
- Test the three mention kinds, the hover-card data and unknown names,
and accept manual entry anchors in the native prose destination check
why: The badges, [source] link and permalink beside a declaration were top-aligned with its code. A formatted native signature pads its code by 0.25rem, so on a one-line declaration such as TypeScript's Server.sessions the controls sat 4.5px above the line's center. Badges aligned by their own baseline sat 0.5px high on every other header, and a heading with a taller line box drifted by more. what: - Align the row's first baselines for native headers at every width and for Python and MCP headers above the 36rem container breakpoint, so controls follow line one of a wrapped signature and ignore the code's padding; center the badge slot on the line the text controls share - Leave the vendored column layout, header height and wrapping as they were; a control group on a row of its own is not moved - Add checkDeclarationControlAlignment to check-native-layout.mjs. It loads TypeScript, Go, Python, Julia, C#, Kotlin, Scala and MCP pages at 1440, 768, 500 and 390px and requires every control beside the code to sit within 1px of the center of the first signature line, and controls that wrap onto their own rows to be level within each row. check-dev.mjs runs it with the signature layouts and the sample loop checks the API probe header - Replace the top-edge assertion in checkNativeSignatureGeometry with a center assertion, and replay the observed hosted Scala geometry as the rejected case Largest control offset from the first line's center, origin/main then this change, measured in Chromium against the dev server: - TypeScript, Go, Julia and C# formatted methods: 4.50px, then 0.00px at 1440 and 768px; TypeScript client.toString at 390px: 4.00px, then 0.00px - Python method and class, Kotlin type: 0.50px, then 0.00px - MCP tool header: 0.50px, then 0.00px; its type definition headings, whose 600-weight face makes a 25px line: 1.80px, then 0.70px - Header heights, code boxes and the stacked layouts are unchanged
why: A prose table row's permalink sat at its first cell's right edge, far from the label it links, and appeared inconsistently with the section permalink. Typography also zeroes the outer padding of the first and last columns, so labels touched the table edge and the row's hover background. what: - Keep the row permalink at the end of its first cell's last line, out of flow so it neither widens the column nor wraps onto its own line - Hide it until the row is hovered, focused or targeted, with the section permalink's theme colors - Give markdown table cells a 0.75rem inline inset, first and last columns included; component tables keep their own padding - Add checkRowPermalinks to check-dev-sample.mjs and run it from the sample and full check-dev flows. Rust, tmux and Ruby tables at 1440, 768 and 390 px in light and dark must put the permalink within 0.5em of the last text line, share one inset across columns, stay quiet at rest, and appear legibly on hover, focus and target
This was referenced Oct 18, 2026
why: `Selection.where(criteria: WhereOf<Model>): Selection<Model>` showed `Model` as plain text. The TypeScript extractor never recorded the parameters of an interface, class or alias, so the linker had nothing to bind, and every port that did record them left the name unlinked or resolved it against a global of the same name. what: - Add `TypeParameter` (name, modifier, kind, bound, default, doc) and `typeParameters` on symbols and signatures. `typeParametersOf` returns the stored list or reads `typeParams` apart per language, so models extracted earlier keep working. - Record container and alias parameters in the TypeScript, Rust, Go, Java and C# specs, fold Rust and C# `where` bounds into the parameter they restrict, and read `@typeParam`, `@template`, `@tparam`, Javadoc `@param <T>` and C# `<typeparam>` onto the parameter. - Extract parameters for Kotlin and Scala (bounds, variance, `where`), Python (PEP 695 lists, `Generic[...]` bases, module TypeVars and the variables a function binds), C++ (class and member template lists, `\tparam`) and Swift (generic parameters and constraints from the symbol graph). - Bind names in one place, `SymbolIndex.typeParameterBindings`: the signature, then the enclosing declarations, honouring Java's static boundary, Rust impl self types and Go receivers by position. Signature and prose links share it, so a bound name links to the anchor `<owner>.type-parameter.<name>` on its declaring page. - `<typeparamref>` becomes a `typeparam` reference that never falls back to a global lookup. - Guard against the C# grammar reading a base type's arguments as a parameter list after a parse error. - `inheritedTypeParameters` lists the enclosing parameters a member's types use, from the same links.
why: A reader of `Selection.where` could not tell what `Model` was. Type parameters were neither documented nor linked, in any port. what: - List a symbol's own parameters under "Type parameters", each with a stable anchor, its bound or constraint, default and documentation. A member page lists the enclosing parameters its signature uses, linked to the owner's anchor and naming the declaring type. - Link each occurrence in native, formatted-layout and unformatted signatures, in bounds, and in prose code literals. Layout ranges that keep a bound name out of the global lookup now link it to its declaration. - Add the section to the on-page contents and to the Markdown export, built from the same bindings as the HTML. - Give type-parameter links `api-type-parameter-name` and a title naming the declaring type, so parameter-name checks keep counting parameters. - Add `check-dev.mjs --type-parameters`: it follows the links on member pages in five ports to the owner's anchor, and compares the Markdown twin. The default browser run includes it. - Update the tests that expected a bound name to be unlinked.
why: The extractors now record type parameters, and the page tests that compare a fresh extraction with the committed model require the two to agree. what: - Regenerate ts, rs, go, csharp, java, kotlin, scala and py from the revisions each model already records, with `gen-api-model.mjs`. The existing models were produced by older extractor versions, so a few unrelated fields move: signature key order in Rust and Java, one C# doc comment and one Java record accessor. - Merge only the type-parameter fields into cxx.json: the local Doxygen is 1.14 and rewrites inline code in other doc comments. - Leave swift.json: `swift build` does not run here (missing libxml2.so.2). Ruby, Julia, Lua, PowerShell and F# come from native artifacts or sources that were not rebuilt; Ruby and Julia are read from their recorded `typeParams`.
why: The manual page for bind-key showed `C-b c` and `c` as code, the same as `new-window` or a flag. A reader could not tell the key to press from the command it runs, and the default binding tables listed keys as bold text. what: - Add tmux-keys.ts with the key grammar from tmux's key-string.c and a policy for how much context a literal needs: modified keys qualify on their spelling, key names in the manual, and every key in a key table, the default binding list or the bind-key, unbind-key, send-keys, send-prefix and list-keys descriptions - Add markTmuxManualKeys, which turns those literals into kbd, nests one kbd per key inside a kbd for a sequence such as C-b c, keeps permalink ids, and drops mandoc's quotation marks around a cap - Leave format strings, option values, command names and bare letters elsewhere in the manual as code - Draw a key cap in the prose theme from the surface and edge tokens, so light and dark share one rule; the layer is utilities because the typography plugin's own kbd rule sits there - Test the grammar and the contexts, the bind-key sequence, the key tables of both pinned versions, command pages against the full manual, and the plain-text Markdown export
why: Guides name keys such as C-c, Enter and Up in inline code, and the attaching guide spelled the detach binding as bold Ctrl-b and d. They read as code beside the manual's key caps. what: - Add a rehype plugin, before the API linker, that marks inline code as kbd when tmux-keys.ts says it names a key: modified notation, a list that holds one, or a name beside "key" or "press" - Keep the word Enter typed into a pane as code - Write the detach binding as `C-b d` with the keys spelled out - Document the convention in WRITING.md; Markdown twins keep backticks - Test the plugin on fragments and on the key mentions of the real guides, topics and port pages
why: Unit tests read the transformed HTML. They do not show that the rendered bind-key page nests the caps, keeps new-window a link, and draws a legible cap in both colour schemes. what: - Add check-tmux-keys.mjs: bind-key renders C-b c as a kbd around two kbd, no key stays code, the command links remain; caps have an outline and raised edge, use the code face, fit 1440 and 390 px and reach 4.5:1 contrast in light and dark - Read the default bindings, the KEY BINDINGS modifiers and two guides, including the word Enter that stays code - Run it in the sample and the full browser matrix, with its pages compiled during setup, and alone with `check-dev.mjs --tmux-keys`
why: A preview link that leads with a kind badge ([M], [C]) drew its dotted underline under the badge letter as well as the name. A text decoration on an ancestor reaches every in-flow descendant, and the badge cannot cancel it, so the line has to sit on the name instead. what: - Put the dotted line on the link's name (.api-symbol-link__name, or the code name in "In other ports") and leave the anchor undecorated when a badge leads it - Drop the prose underline from badge links that are not previews, and let their name draw it - Wrap MCP description link names in .api-symbol-link__name, as prose links already do - Check in a real browser that each badge's rendered box matches the same page drawn without decorations, at rest and hovered, and that the name has exactly one dotted underline; cover prose, headings, tables, MCP, API equivalents, related lists, sidebar, tree and search results - Extend the happy-dom decoration test to the badge link shapes
why: A badge aligned to its name's baseline, so its centre sat below the text centre and the gap to the name varied by context. Measured in Chromium before the change: the centre was 3px low beside body text and a code chip, 8px low in an h2, 5.5px in an h3, 1.5px in a table, 2.5px in related cards and 1.3px in search results. The gap was .3em in prose (4.8px to the chip, 10.8px to its text), .35em in MCP cards, .37em in API equivalents, .6em in related cards, .67em in related contents and .2em in search results. what: - Centre the badge on the link's name with align-self while the name keeps the baseline, so the link's height and its text do not move - Set one .25em gap between a badge and the name in prose, MCP, related, equivalent and search links; navigation and sidebar rows already centre and keep their 4px - Lay out search result names as a flex row so the badge and name share the same gap - Move the related-card summary indent to the new badge and gap width - Assert in the browser check that each badge centre is within 1px of its text centre, the gap is about .25em, and a one-line link stays within its line height
why: Hovering the "Runtime support" card on the workspace overview drew the preview's dotted underline under its title and body. The card links to reference/compatibility/, which the preview policy accepts as a reference page, so the whole block link was marked and a[data-link-preview] painted the line through its heading and paragraph. Cards that link to guides were not matched, which is why only this one showed it. A card already summarizes its destination, and only an inline text run carries the dotted line. what: - Make a link that wraps blocks, or a .doc-card, ineligible for a preview, and remove a mark the parser added before its blocks arrived - Check in a real browser, after hovering every block link on the workspace, port and MCP overview pages, that each dotted link line sits on an inline link without block children, with a prose page as the control that inline preview links keep theirs - Cover the card and block-wrapping link shapes in the preview policy and decoration tests - Test the badge check's dotted line with endsWith
why: A member body (`dl.py > dd`) is padded .75rem, but its first and last children carried their own margins on top of that. A body holding only "In other ports" opened 20px below its header and closed 12px above its border; a trailing description paragraph or example did the same at the bottom, and a leading parameter list added a divider and padding before its label. Each case was patched on its own (the Methods heading, then the Description and Examples headings), so the next shape of body repeated the bug. what: - Trim the top margin of a body's first child, and the bottom margin of its last, through up to three wrappers, in one rule set in api.css - Drop the divider and padding of a field list that opens the body - Drop the bottom margin of the last parameter heading, which sits in a grid item the chain cannot reach - Remove the narrower rules this replaces from global.css and api.css - Add checkMemberBodyInsets to check-native-layout.mjs: it opens member bodies on Go, Julia, Ruby, Python, TypeScript, Swift, C#, Java, PowerShell and MCP pages at 1440 and 390px, finds the first and last painted content, and requires both insets to match the padding and each other within 1px - Run it from check-dev.mjs, alone with --member-bodies - Rewrite declaration-body-spacing.test.ts around the new rules, including the sibling spacing they must leave alone
…es in code why: On /py/latest/workspace/topics/, `tmuxp load`, `tmuxp freeze` and `shell_command_before` rendered as plain code, although the CLI Manual and the Configuration reference have an entry for each. The prose linker only knew library symbols, MCP names on MCP pages and tmux names, so workspace names never reached a resolver. Where a workspace name was linked, it went to the wrong place: `freeze` and `search` led to a library function and the Python standard library, `session_name` to the tmux format variable, and Ruby's `load` to its API. what: - Index each port's CLI Manual commands, option rows and configuration key rows from the workspace pages (scripts/gen-workspace-entities.mjs into site/src/data/workspace-entities.json), anchored on the rows the rendered tables serve; createRowAnchors now numbers rows for the page and the indexer alike - Resolve `<program> <command>` on any page of the port that ships the program, and a bare command, option or key on a user-facing workspace page, through the mention pipeline that badges tmux names: the same badge markup, inline links without a badge inside a longer code run, plain links in Markdown exports - Let the page's own product and port win: a workspace name outranks a library symbol, a standard-library name and a tmux name, while Internals pages and the language API index keep their library symbols - Leave a heading, the first cell of the row that defines a name, and a command's own page plain rather than sending them elsewhere - Prefer a key's entry on the page it is written on, then the first in reference order; a name that is both a command and a key follows its section, or its quotes - Link tools and prompts named inside a longer MCP code run, and resource URIs on workspace pages, which a library symbol never shares - Add a key badge (colon mark, property palette) beside the tmux kinds - Share the linked-version rule between the prose linker and the new links as linkedVersionFor - Check the index in the medium loop and the publication audit, and walk every workspace and MCP page of every port in a test that fails on any code literal naming a command, option, key, tool or resource that renders plain; its negative case renders the maintainer's page without linking and names the three reported literals Measured on the original pipeline: 178 such literals rendered plain across the nine ports with workspace pages, and none do now.
…iptions why: The Python server publishes its docstrings verbatim, so create_session printed `:func:` role text, -eKEY=VALUE and show-environment rendered as unstyled code, and show-environment had no link. The description renderer only knew Markdown and MCP tool names, so every port left its tmux commands, options, hooks and format variables unlinked, and meta descriptions carried raw backticks. what: - Rewrite roles and directives before Markdown renders them: a role becomes a link to the API reference page or MCP tool page that documents its target, or clean code when none exists (a private helper has no page and no extracted source line); :doc: links the upstream docs site; a warning directive becomes the site aside - Run descriptions through the prose mention pipeline so tmux commands, options, hooks and format variables link through tmuxEntityHref with the same badges and previews as prose pages, and drop its links to the object being described - Add a symbols opt-out to that pipeline: an argument named logical_name is not evidence for the library attribute of that name - Give inline literals the API literal chip instead of bare code - Use plain-text summaries for meta descriptions, search and previews - Move the admonition kind table next to the aside it feeds - Sweep every port's tool, prompt and resource descriptions and schema prose through the real renderer, failing on raw roles, directives, backticks, unstyled literals or unlinked tmux names, with negative cases on the origin/main rendering and clean controls - Export a described tmux name as a plain Markdown link with no badge
…closure
why: The line under the page heading ("Module session") repeated the module
that the source and package disclosure already lists, and the maintainer
asked for it gone. It also carried the only visible full name and its copy
button, which Swift and PowerShell pages (no module to show) used alone.
what:
- Remove the line under the heading; the disclosure's module row is the
same value, since both read the owner's module
- Label that row for the port (Package for Java, Kotlin, Scala and Go;
Namespace for C#, F# and C++; Module otherwise), as the line did; name the
distribution row Distribution where both would say Package
- Add a Qualified name row at the top of the disclosure with the copy
button, so every page keeps its full name, including the ones with no
module; drop the expansion control, which a wrapping row does not need
- Test the placement with negative controls, and move the browser checks
for the Kotlin and Java package rows to the disclosure
…n the system why: Two badge-like pieces were still on the old look: the version chip beside the kind in the reference eyebrow (an 11px monospace grey pill on a surface fill) and the "(struct)" or "(cmdlet)" suffix in Other languages. what: - Draw the version as a grey modifier chip on the legend chip's box, so the eyebrow row reads as one family; the three eyebrows (API reference, MCP reference and tmux catalog) share the markup - Write the other-language kind as the plain kind word that member lists use, outside the link, instead of parentheses inside it
why: API pages showed an `overload` badge on types. The Rust page for `session.Session` rendered "struct overload". Every extractor stamped the modifier whenever two declarations landed on one id, and several languages put one type or member under one id more than once: - Rust: `struct X` or `enum X` plus its `impl X` blocks, in the same file (spec.ts) or in another one (the modifier unions in mergePartials) - TypeScript: `class X` plus the `interface X` that merges into it, and a `static` and an instance property of one name - C#: `X` beside `X<T>`, which share the id `Ns.X` - Java: a field beside the accessor named after it A type written in several parts is one type, and a field is not an overload of its accessor. The Kotlin/Scala, C++ and Swift adapters had the same unguarded stamp and no committed symbol reached it yet. The badge counts per model before and after: rs.json 125 -> 1 (94 struct, 29 enum, 1 class removed) ts.json 8 -> 3 (4 class, 1 attribute removed) csharp.json 36 -> 34 (2 class removed) java.json 73 -> 72 (1 attribute removed) what: - Add overloads.ts: `canOverload` (a method or function with more than one signature), `markOverload`, `retractMisplacedOverload` and `misplacedOverloads`. - Stamp through `markOverload` in the spec, Kotlin/Scala, Doxygen and symbol-graph adapters, and in mergePartials, which also drops an `overload` that a merged part carried in. A merged type keeps its signatures, docs and other modifiers. - Make gen-api-model.mjs refuse to write a model that holds a misplaced overload, so a new adapter cannot reintroduce it quietly. - Regenerate rs, ts, csharp and java at the revisions recorded in each file. The diff is only the removed modifier: each file equals its previous contents minus `overload` on non-callables. kotlin and scala regenerate unchanged from the same revision as a control. - Test each stamping path (Rust same-file and cross-file, TypeScript merging and static/instance, C# arity variants and records across files, Java field and accessor, Kotlin expect/actual, Doxygen, symbol graph) beside a real overload set that keeps the modifier. The type cases fail against the previous adapters. - Check all fifteen committed models: no type or lone signature carries `overload`, and the ports with real overload sets still have them.
why: The control shifted a modifier chip on the Rust struct page, whose only modifier was a stamped `overload` that the model no longer carries on types. what: - Use the TypeScript async method page, which carries a real `async` modifier, for the modifier control
…in the docs sidebar why: The MCP sidebar rule applied to the reference tree only. The documentation sidebar lists the same tools, prompts and resources under sections named for them, each row still carrying a "t" or a "P". what: - Flag the links of a protocol section whose objects share one kind as quiet and draw no mark on them; keep the kind on the link as its description for assistive technology - Keep the marks in a section that mixes kinds, and leave the language API groups of the same sidebar as they were - Hold the rule in a small shared helper, and test it on its own because no advertised set mixes kinds today
…nd word why: The A to Z index named each symbol's kind in faded text, a different treatment from the same word beside a name in a member list. what: - Render the kind with the plain kind word that lists use, with "type alias" spelled as in the cards
why: Run against the served tip, check-page-readiness failed three ways: it looked for marks in the MCP sidebar, which now draws none under a one-kind group; it held cards to the prose link's quarter-em gap and line-box centre, where they now keep half an em and centre on the capitals; and its first-frame underline probe looked for a word one box shallower than the legend chip's. what: - Measure the navigation tree link on an API tree, where marks remain - Hand the MCP index card and group heading to check-badge-centring, which measures them from pixels with a negative control, instead of the older gap and centre rule - Find the first-frame dotted underline on any label inside a linked chip - Keep the bounded sample covering every check the gate holds, and give a cold reference page a minute to compile in the dev server - Describe the new coverage in CONTRIBUTING.md
why: On master, ts, rs, ruby, lua, cxx, go and dotnet (the C# and F# docs ports) refuse a tmux daemon older than 3.3a, including unpatched 3.3, for the core, MCP and workspace alike. Their pages still said "tmux 3.2a or newer", so a reader on 3.2a was walked into a refusal. Each port's master README states 3.3a as its floor. what: - Change "tmux 3.2a or newer" to 3.3a on the ts, rs, ruby, lua, cxx, go, csharp and fsharp pages: setup lines, MCP guides and examples, and the workspace "Runtime and configuration support" pages - Split the shared workspace installation sentence by port: ts, rs, go, csharp and cxx say 3.3a; java and swift keep 3.2a - Change the prerequisite line the complete API examples print for csharp, cxx, fsharp, go, lua, ruby and rust, in each *-examples.ts template and in the generated intro text of its API model, which is the substitution a regeneration at the unchanged pin makes - Change the homepage quickstart prerequisites for ruby, lua, ts, rs and go Left as written: - java, kotlin, scala, swift, julia and powershell stay at 3.2a until their merges land; py, tmuxp and libtmux-mcp are unchanged - Raw tmux shell scripts on the shared guides, concepts, topics and examples pages describe tmux itself - "run on Linux with tmux 3.2a and 3.7c" on the ruby, lua and fsharp capture pages and the rs MCP inspect-sessions example records the runs the receipts in site/test/fixtures hold, which list 3.2a and 3.7c; those need a new native run before the text can change - The rs standard-library-core page documents a prototype pinned at an older revision whose own README says 3.2a - Text extracted from port source or staged from port repositories at the documented pins: the TSDoc examples in ts.json, Go's Connect summary and the "(tmux 3.2a or later)" field notes in go.json, and the port-guides caches. Every API-model pin still predates the floor raise, so these change with a pin bump, not by hand - tmux manual and catalog version names and tmuxp's 88-colour note
This was referenced Oct 18, 2026
…en mark why: A table row's permalink is built from its first cell's text, and that text now included the letter of the hidden kind mark in front of a tmux name. The published #tmux-version-compatibility-pane-title-changed-hook on the tmux options and hooks page became #…-hpane-title-changed-hook, breaking every existing link to it. what: - Leave aria-hidden elements out of the label a row anchor is built from - Test a row whose first cell leads with a hidden mark
…mcp/ page why: The Python install picker was checked on the cross-port /mcp/ page, which now only redirects. Python's own MCP page comes from its Sphinx build, which an assembled preview does not carry, so no built page holds the picker any more. what: - Render the picker for Python and check it still offers uvx, pipx and pip with a cooldown control; it now runs without a built site
why: libtmux-swift's master raised its core, MCP and workspace minimum to tmux 3.3a (libtmux/libtmux-swift#47), and the Swift pages document master, but they still told readers tmux 3.2a was enough. what: - State tmux 3.3a in the Swift guide, MCP and workspace walkthroughs, the workspace compatibility page and the Swift API example template and model data - Move Swift to the 3.3a sentence of the shared workspace installation page; Java keeps 3.2a until its own minimum moves
…nk ceiling why: "List every parameter in every port" (db9c274) gave Ruby and Swift member pages a parameter list, and each listed parameter shows its type. Type names already plain in the signature, such as Swift's Self and Ruby's String, Integer and untyped, now appear plain a second time in that list, so check-type-links counted more plain names than its ceiling: Ruby 2051 to 2910 and Swift 1959 to 2342 (CI run 39176242344). Resolution did not get worse. On 160 sampled Swift reference pages the signatures render the same plain names as libtmux.org at 7ffc04b, linked type names rose from 147 to 154, and every added plain name sits in a parameter row (capturecursor(_:_:) goes from 2 to 4 plain Self, the two new ones under Parameters). what: - Record the counts CI measured as the Ruby and Swift ceilings; every other port's ceiling is unchanged and the ceiling still only comes down
tony
added a commit
that referenced
this pull request
Oct 20, 2026
why: The type-link ceiling only comes down, but a passing run printed one total, so a ceiling raised during a wave stayed high until someone ran --list by hand. Every name of headroom is a name that can regress without failing the gate. The ruby and swift ceilings were raised for #157's parameter lists before the rendering fix landed, and CI had no output to lower them from. what: - After a passing check, list each port whose count is below its ceiling, with the recorded and measured values - Add a negative case: a site under its ceiling passes and names the headroom, and the at-ceiling control prints no headroom
tony
added a commit
that referenced
this pull request
Oct 20, 2026
…red counts why: The ceilings sat far above the plain type names each port renders, so that many names could fall back to plain text without failing the gate. The ruby and swift entries were raised for #157's parameter lists before the rendering fix landed; kotlin, scala and others predate fixes that resolved most of their names. CI on efbdddb (run 39358987283) measured every port below its ceiling. what: - Set each ceiling to that run's count: py 262 -> 146, ruby 2910 -> 2109, lua 98 -> 74, julia 159 -> 108, ts 91 -> 18, rs 625 -> 420, go 128 -> 2, java 137 -> 51, kotlin 2978 -> 24, scala 3252 -> 644, fsharp 252 -> 36, cxx 342 -> 46, swift 2342 -> 1680 - Leave csharp at 44: the run measured 18 on the alpha.20 model, and this push regenerates it at alpha.22; the next run's headroom report measures it. powershell stays at 0
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes from the 2026-10-18 review of the reference pages, on one branch so they share one CI run, one preview and one deploy. #158 to #163 were folded in here; each closed PR keeps its full discussion and evidence. Later fixes from the same review land on this branch too.
Sticky member and group headings
Long member lists scrolled the Members heading and the active group heading out of view, so a reader lost track of which group a member belonged to. Both headings now stay stuck below the site header and section row, and release at the end of the Members section. This was P0 6, held back from #156 until review found the problems below.
What a reader gets
Fixed after the first review
Verification
node site/scripts/check-dev.mjs --api-stickyruns focus, link, repeated-link and typed-hash scenarios in Chromium, Firefox and WebKit at 1800 and 390px. Its controls fail in each engine without the focus clearance, without the typed-hash handling, or with an unpositioned Members bar.site/test/api-page-contents.test.ts: 69 tests, including the new cases that fail on the unfixed sources.Known gaps
Keyword-argument literals stay unlinked (was #158)
A prose literal shaped like a keyword argument (
name:orname: value) stays plain in HTML and Markdown exports. The RubyServerreference had linkedcancel:toCancellation#cancel,clients: truetoSnapshot#clientsandwhere:toSelection#where. A bare name such ascancelstill links. Test:inline-reference-appearance.test.ts, which fails without the rule.Every parameter listed in every port (was #159)
Python, Swift, Ruby and Lua listed only the parameters a doc comment described, so Ruby
Server.startshowed no Parameters section. Every port now lists each declared parameter with its type, and each parameter name in the signature links to its entry.@overloadstub's.self(or Pythoncls, or Luaself) is the receiver and is neither listed nor linked.tmux CLI Manual and C source reference in the sidebar (was #160)
The tmux sidebar ends with CLI Manual and C source reference for the selected tmux version, and the overview cards use the same names as the documentation picker. Test:
documentation-navigation.test.ts.tmux commands, options, hooks and format variables badged and previewed (was #161)
tmux names in guides, port prose and API doc comments render like API mentions: a kind badge (
$command,#format,ooption,hhook), a hover card from the pinned tmux manual, and a link to the manual entry.tmuxEntityHref(kind, name, version)intmux-links.tsresolves all four kinds.list-clients,session_attachedanddestroy-unattachednow carry badges.session_namestill links to the Rust API field, because library symbols outrank tmux names.Tests:
tmux-mentions.test.ts(42) andlink-preview.test.ts. Screenshots are in #161; delete thetmux-mention-badges-screenshotsbranch after review.Declaration controls level with the first signature line (was #162)
The badges,
[source]and permalink beside a declaration are now level with the centre of the signature's first line, also when the signature wraps. On TSServer.sessionsthey sat 4.5px high, because the formatted signature's padding was counted. The fix is insite/src/styles/api.css; the vendored gp-sphinx CSS is untouched.Gate:
checkDeclarationControlAlignmentincheck-native-layout.mjs, run bycheck-dev.mjs --reference-layout. It measures 48 headers across TS, Go, Python, Julia, C#, Kotlin, Scala and the Rust MCP page at 1440, 768, 500 and 390px. It fails on main (-4.50px) and passes here (worst 0.70px, on MCP type headings).Table row permalinks trail the cell text (was #163)
A prose table row's
¶now sits right after the first cell's last text line, hidden until the row is hovered, focused or targeted. Markdown table cells share a0.75reminline inset, so the first column no longer touches the table edge.Gate:
checkRowPermalinksincheck-dev-sample.mjschecks the Rust, tmux and Ruby CLI tables in light and dark at 1440, 768 and 390px. It fails on main ("permalink starts 39.8px after its text") and passes here.Type parameters documented and linked
ModelinSelection.where(criteria: WhereOf<Model>): Selection<Model>was plain text, because the TypeScript extractor never recorded the type parameters of interfaces, classes or aliases. Extractors now record them, and one linker binds every occurrence to the declaring page's new Type parameters section. Member pages say which declaration owns the parameter, and Markdown exports carry the section.Generic[...]bases and TypeVar bounds. C++ covers templates, concepts and\tparam. Julia and Ruby are read from their recorded parameters.swift buildfails on this host (missinglibxml2.so.2). F# models carry no generics, so'Tstays unlinked.cxx.json, because the local Doxygen differs from the one that produced it.check-dev.mjs --type-parametersfollows the links to the declaring anchor on ts, py, rs, go and java, and fails on the oldts.json.tmux key sequences render as keyboard keys
The tmux manual and guides now render key sequences such as
C-b cas<kbd>caps (<kbd><kbd>C-b</kbd> <kbd>c</kbd></kbd>) instead of code.key-string.c(site/src/lib/tmux-keys.ts). ModifiersC-,M-andS-,^X, and the key names the manual spells (Enter, Escape, BSpace, F1–F12, the keypad and so on) count.tmux-keys.test.ts(95 cases) and the manual and plugin tests.check-dev.mjs --tmux-keysasserts the bind-key page's DOM.Kind badges: no dotted underline, centred, one gap; cards are not previews
M/C). The name now draws the underline and the badge draws none. Non-preview badge links draw their solid underline on the name only.align-self: center) with a.25emgap everywhere. The gap had been anywhere from.2emto.67em, and badges sat 1.5px to 8px low in body text, headings and tables. Nav and sidebar rows were already centred and keep their 4px gap.reference/, so the preview script marked it and painted a dotted underline across the whole card on hover. Links that wrap blocks, and.doc-card, are no longer preview candidates.checkKindBadgeLinksasserts one dotted underline on the name, the badge centre within 1px and a gap of about.25em.checkBlockLinksUndecoratedhovers every block link on the workspace, port and MCP overview pages, with a prose page as the inline control.inline-reference-appearance,link-preview-paintandmcp-referencealso fail on the old source.Member bodies: padding alone sets the inset
On the Go
Server.ConfigFilepage a member body had 20px above its first line and 12px below its last. Elsewhere it was 12px above and 20px below. The cause was a top or bottom margin on the first or last child (a paragraph, a field list or an example) added to the body's 12px padding. One rule insite/src/styles/api.cssreplaces the earlier narrow fixes: edge children ofdl.py > dd, down to three levels, lose their outer margin. A field list that opens the body also loses its divider and top padding. Spacing between siblings is unchanged.Gate:
checkMemberBodyInsets(check-dev.mjs --member-bodies) measures 17 pages across Go, Julia, Ruby, Python, TS, Swift, C#, Java, PowerShell and MCP at 1440 and 390px. It requires the top inset, bottom inset and padding to agree within 1px. Before the fix it failed on 12 pages (76 body lines); now it passes on 54 bodies.Workspace commands, options and keys link to their reference
On the Python workspace topics page,
tmuxp load,tmuxp freezeandshell_command_beforewere plain code. Workspace names never reached a resolver, and where one did link it often went to the wrong target:freezeto a library function,searchto the Python standard library,session_nameto a tmux format.scripts/gen-workspace-entities.mjsgeneratessite/src/data/workspace-entities.jsonfrom each port's CLI Manual commands and option and key tables. The anchors are shared with the rendered rows.--checkruns in the test loops.<program> <command>(e.g.tmuxp load,tmux-workspace load) links on any page of a port that ships that program. A bare command, option or key links on user-facing workspace pages, and the page's own port and product win over library, standard-library and tmux names. Internals and API-index pages keep their library links.$badge, optionso, and keys a new:badge. MCP tool and prompt names inside longer code runs now link without a badge. Markdown exports keep plain links.workspace-mentions.test.tswalks every workspace and MCP page through the real pipeline against an independent oracle, and fails on the reported page without the linker.MCP tool pages: rendered descriptions and API-style arguments
On the Python
create_sessionpage, theenvironmentargument showed a raw:func:libtmux_mcp._utils._coerce_dict_arg.-eKEY=VALUEwas unstyled,show-environmentwas not linked, and the Arguments list looked unlike the API pages._coerce_dict_arghas no page... warning::renders as an aside.symbols: falseflag stops an argument named like a library attribute from linking to it.name? = default (type) #.Record<string, T>shows as a map.create_sessionare unchanged, and folded variant anchors stay as aliases.mcp-descriptions.outer.test.tssweeps every port's MCP descriptions and fails on main's rendering, with clean controls.tmux examples build up to their scripts
The native tmux example pages opened with the complete script. Each page now starts with a short concept paragraph and works through numbered steps, one tmux command or idea at a time, each with a small block and links to the manual. "The complete script" comes last and is tested unchanged.
excerpt="<title>"orfile="<path>"plusfromandtotext anchors. The build fails if thefromanchor matches no line or more than one, or iftois missing or abovefrom. Markdown exports resolve the same excerpts. 29 tests cover this, including mutation checks on each guard.Indentation kept in Javadoc examples
The Java
TmuxEnvironmentexample rendered flush left. The Javadoc extractor stripped the*margin and every space after it, so the model storedtry (…) {\nSession mine = …. It now strips only the margin, so relative indentation inside<pre>{@code}survives, and the Java model is regenerated.examples[].codechanged in the regenerated model.\\codelisting onto one line. No committed C++ comment uses\\codeyet.scripts/check-example-indentation.mjscompares each comment-derived example in every committed model against its source, and runs intest-all.sh. A negative script plants the Java shape.tmux options, hooks and formats catalog
/tmux/<version>/catalog/addsoptions/,hooks/andformats/indexes and a page per entry, in the C reference's layout, with "Used by" lists and hover cards. tmux 3.2a has 104 options, 61 hooks and 200 formats; 3.7c has 153, 68 and 258.tmuxEntityHrefnow sends options, hooks and formats to catalog pages. Commands stay in the CLI Manual.scripts/gen-tmux-catalog.mjs --checkand its negative script run in the test loops. A crawl of 1,335 catalog pages found no broken links.Verification of the combined branch
pnpm run lintexits 0. The two remaining warnings are in files this branch does not change.